/*
 * Datafile.cpp
 *
 *  Created on: Apr 19, 2011
 *      Author: Marc Kampherbeek
 */

#include "Datafile.h"
#include "DataBlock.h"

Datafile::Datafile(Inode* i, int t) {
	//cout << "CONSTRUCTOR DATAFILE" << endl
	cout << "changed here" << endl;
	fs = FS::getInstance();
	inode = i;
	opened = 1;
	iopos = 0;
	bool isUser = false;
	bool isGroup = false;
	if(inode->getOwner() == fs->getUserID()){
		isUser = true;
	}
	if(inode->getGroup() == fs->getGroupID()){
		isGroup = true;
	}
	cout << "still here?" << endl;
	int type = t;
	cout << "type: " << t << endl;
	if(type >= O_TRUNC){
		cout << "D-1" << endl;
		btruncate = true;
		type -= O_TRUNC;
	}else
		btruncate = false;
	if(type >= O_EXCL){
		cout << "D-2" << endl;
		bexclusive = true;
		type -= O_EXCL;
	}else
		bexclusive = false;
	if(type >= O_CREAT){
		cout << "D-3" << endl;
		bcreate = true;
		type -= O_CREAT;
	}else
		bcreate = false;
	if(type >= O_WRONLY){
		cout << "D-4" << endl;
		if((isUser && inode->modeString().substr(1,1).compare("r") == 0) ||
				(!isUser && isGroup && inode->modeString().substr(4,1).compare("r") == 0) ||
				(!isUser && !isGroup && inode->modeString().substr(7,1).compare("r") == 0) ||
				fs->getUserID() == 0){
			bwrite = true;
		}else{
			cout << "you have no permission to write this file" << endl;
		}
		type -= O_WRONLY;
	}else
		bwrite = false;
	if(type >= O_RDONLY){
		cout << "D-5" << endl;
		if((isUser && inode->modeString().substr(2,1).compare("r") == 0) ||
				(!isUser && isGroup && inode->modeString().substr(5,1).compare("r") == 0) ||
				(!isUser && !isGroup && inode->modeString().substr(8,1).compare("r") == 0) ||
				fs->getUserID() == 0){
			bread = true;
		}else{
			cout << "you have no permission to read this file" << endl;
		}
		type -= O_RDONLY;
	}else
		bread = false;
	/*if(type == O_RDONLY || type == O_WRONLY || type == O_RDWR){
		permissions = type;
	}else{
		cout << "DATAFILE: PERMISSIONS NOT RECOGNIZED" << endl;
	}*/
}

Datafile::~Datafile() {
	opened = 0;
	iopos = 0;
	fs = 0;
	delete inode;
}

ssize_t Datafile::read(void* buffer, size_t count){
	if(!bread)//no read permission
		return 0;
	if(iopos + count > inode->getSize())//out of file bounds
		return 0;
	int done = 0; //keep track of the amount of data written to the buffer
	int startBlock = iopos / DSKBLKSIZ; //startblock within the inode
    int startpos = iopos; //keep track of the value of iopos when this method started
	//reading cycle
	//start might be at the last byte of one block, so we have to calculate iopos along with it
	for(int i = 0; i <= ((startpos % DSKBLKSIZ) + count) / DSKBLKSIZ; i++){ //in case it covers multiple blocks
		int currentBlock = startBlock + i;
		int readFromBlock; //amount we need to read from current block
		if(i == ((startpos % DSKBLKSIZ) + count) / DSKBLKSIZ){ //last block to read from?
			if(i == 0){
//				cout << "determined at point a" << endl;
				readFromBlock = DSKBLKSIZ - (startpos % DSKBLKSIZ);
				if(count < readFromBlock){
//					cout << "determined at point b" << endl;
					readFromBlock = count;
				}
			}else{
//				cout << "determined at point c" << endl;
				readFromBlock = ((startpos % DSKBLKSIZ) + count) % DSKBLKSIZ;
			}
		}else{
			if(i == 0){
//				cout << "determined at point d" << endl;
				readFromBlock = DSKBLKSIZ - (startpos % DSKBLKSIZ);
			}else{
//				cout << "determined at point e" << endl;
				readFromBlock = DSKBLKSIZ;
			}
		}
//		cout << "need to read " << readFromBlock << " bytes" << endl;
		//determine block number on disk
		int diskBlock = inode->getDiskBlockNr(currentBlock);
		//get datablock so it can be read
		DataBlock* dataBlock;
		if(diskBlock != 0){
			dataBlock = fs->getDataBlock(diskBlock);
		}else{
			byte data[512];
			for(int i = 0; i < 512; i++) data[i] = 0;
			dataBlock = new DataBlock(0, data);
		}
		//write data to buffer
		while(readFromBlock > 0){
//			cout << "iopos: " << iopos << endl;
//			cout << "readByte(" << iopos % DSKBLKSIZ << ")" << endl;
			byte b = dataBlock->readByte(iopos % DSKBLKSIZ);
//			cout << "byte: " << b << "(" << (int)b << ")" << endl;
//			cout << "done = " << done << endl;
			//buff[done] = b;
			*((byte *)buffer + done) = b;
//			cout << "buffed passed" << endl;
			iopos++;
			readFromBlock--;
			done++;
//			cout << "done: " << done << endl;
		}
	}
	//cout << "done: " << done << endl;
	inode->setLastAccessTime();
	return done;
}

off_t Datafile::lseek(off_t offset, int start_flag){
	int newpos;
	if(start_flag == 0){
		newpos = 0;
	}else if(start_flag == 1){
		//no changes, but needs to be checked to catch wrong input
	}else if(start_flag == 2){
		newpos = inode->getSize();
	}else{
		return -1; //return error, no changes made
	}
	iopos = newpos + offset;
	return iopos;
}

ssize_t Datafile::write(void* buffer, size_t count){
	if(!bwrite)//no write permission
		return 0;
	inode->setLastModifiedTime();
	return fs->write(inode, iopos, buffer, count);
}
